
#include "sniffer.h"
#include "servidor.h"

/*
 * Função que captura e retorna as
 * interfaces e redes disponiveis no sistema
 */
char *capturaInterfaces(void) {
    pcap_if_t *alldevs, *d;
    char *buffer;

    buffer = (char*) malloc(sizeof (char) * 1024);

    /* The user didn't provide a packet source: Retrieve the device list */
    if (pcap_findalldevs(&alldevs, buffer) == -1) {
        sprintf(buffer, "Error in pcap_findalldevs: %s", buffer);
        return buffer;
    }

    sprintf(buffer, "<mensagem>");

    /* Print the list */
    for (d = alldevs; d; d = d->next) {
        if ((strncmp(d->name, "eth", 3) == 0) || (strncmp(d->name, "wlan", 4) == 0)) {
            sprintf(buffer, "%s<interface>%s</interface>", buffer, d->name);
        }
    }

    sprintf(buffer, "%s<interface>lo</interface></mensagem>", buffer);

    /* Free the device list */
    pcap_freealldevs(alldevs);

    return buffer;
}

/*
 * Função que inicializa captura de pacotes, captura interface
 * de rede e depois aplica a regra definida em filtro.
 * interface Interface de rede que será utilizada para filtragem.
 * filtro Especificação da regra de filtragem.
 * msg Mensagem de erro caso ocorra.
 * retorna um descritor de socket libpcap.
 */
pcap_t* inicializaCapturaPacotes(char *interface, const char *filtro, char *msg) {
    pcap_t* pd;
    uint32_t net, mascara;
    struct bpf_program fp;

    /*
     * Verifica se foi informa uma interface de rede
     */
    if (!*interface) {
        /*
         * Captura a primeira interface disponivel
         */
        if (!(interface = pcap_lookupdev(msg))) {
            sprintf(msg, "<mensagem><error>Nenhuma interface de rede informada.</error></mensagem>");
            return NULL;
        }
    }

    /* 
     * Abre uma interface de rede selecionada para a captura de
     * pacotes e retorna um descritor de socket libpcap.
     * 
     * pcap_t *pcap_open_live(char *device,int snaplen, int prmisc,int to_ms, char *ebuf)
     * 
     * device - recebe a interface de rede informa na função
     * snaplen - tamanho maximo dos pacotes em bytes utiliza-se geralmente a variavel BUFSIZ
     * promisc - define o modo como a interface de rede ira trabalhar (0 - Não Promiscuo e 1 - Promiscuo)
     * to_ms   - tempo de esperar por pacotes em milissegundos antes de ler e sair
     * errbuf  - se algum erro acontecer, é armazena nesta variavel
     */
    if ((pd = pcap_open_live(interface, BUFSIZ, 1, TEMPO_ESPERA, msg)) == NULL) {
        sprintf(msg, "<mensagem><error>Erro ao abrir interface de rede selecionada.</error></mensagem>");
        return NULL;
    }

    /*
     * captura a interface de rede do computador e mascara de rede
     * verifica se foi possivel capturar uma interface de rede
     */
    if (pcap_lookupnet(interface, &net, &mascara, msg) == -1) {
        sprintf(msg, "<mensagem><error>Nao foi possivel obter a mascara de rede para o dispositivo.</error></mensagem>");
        net = 0;
        mascara = 0;
        return NULL;
    }

    /* aqui e compilado o programa filtro de */
    if (pcap_compile(pd, &fp, (char*) filtro, 0, mascara) == -1) {
        sprintf(msg, "<mensagem><error>Falha ao compilar programa filtro.</error></mensagem>");
        return NULL;
    }

    /* aqui é especificado o programa filtro*/
    if (pcap_setfilter(pd, &fp) == -1) {
        sprintf(msg, "<mensagem><error>Falha ao especificar o programa filtro.</error></mensagem>");
        return NULL;
    }

    pcap_freecode(&fp);

    return pd;
}

/*
 * Função que realiza captura de pacotes e envia ao cliente.
 * pd Descritor de socket libpcap.
 * quant Quantidade de pacotes a ser capturados.
 * socket Descritor socket de conexão com o cliente.
 */
void capture_loop(pcap_t* pd, int quant, int socket) {
    int cont = 0;
    const u_char *dados;
    struct pcap_pkthdr header;
    char *msg;

    msg = (char*) malloc(sizeof (char) * 200);

    while (cont < quant) {

        /* realiza captura de pacotes */
        dados = pcap_next(pd, &header);

        /* verifica se os dados capturados são validos diferente de NULL */
        if (dados) {
            struct iphdr *iph = (struct iphdr*) (dados + sizeof (struct ethhdr));
            
            /* Verifica qual tipo de protocolo e extrai dados */
            switch (iph->protocol) {
                case 1:
                    cache_icmp_packet(dados, msg);
                    break;

                case 6:
                    cache_tcp_packet(dados, msg);
                    break;

                case 17:
                    cache_udp_packet(dados, msg);
                    break;

                default:
                    break;
            }

            /* envia dados extraidos ao cliente */
            if (send(socket, msg, BUFFER_SERVIDOR, MSG_WAITALL) == -1) {
                break;
            }

            cont++;
        }
    }

    free(msg);
    if (pd) {
        pcap_close(pd);
    }
}

/*
 * Extrai cabeçalho dos pacotes e retorna em msg.
 */
void cache_ip_header(const u_char *buffer, char *msg) {
    char src_ip[20], dest_ip[20];

    struct iphdr *iph = (struct iphdr *) (buffer + sizeof (struct ethhdr));

    /* Captura endereços de origem e destino dos pacotes */
    strcpy(src_ip, inet_ntoa(*(struct in_addr *) &iph->saddr));
    strcpy(dest_ip, inet_ntoa(*(struct in_addr *) &iph->daddr));

    sprintf(msg, "<srcip>%s</srcip><dstip>%s</dstip>", src_ip, dest_ip);
}

/*
 * Extrai dados dos pacotes tcp e retorna em msg
 */
void cache_tcp_packet(const u_char *buffer, char *msg) {
    unsigned short iphdrlen;
    struct tcphdr *tcph;

    struct iphdr *iph = (struct iphdr *) (buffer + sizeof (struct ethhdr));
    iphdrlen = iph->ihl * 4;

    tcph = (struct tcphdr*) (buffer + iphdrlen + sizeof (struct ethhdr));

    cache_ip_header(buffer, msg);

    /* obtem portas da origem e do destino */
    sprintf(msg,
        "%s<srcport>%u</srcport><dstport>%u</dstport><proto>tcp</proto>",
        msg,
        ntohs(tcph->source),
        ntohs(tcph->dest)
    );
}

/*
 * Extrai dados dos pacotes udp e retorna em msg
 */
void cache_udp_packet(const u_char *buffer, char *msg) {
    unsigned short iphdrlen;
    struct udphdr *udph;

    struct iphdr *iph = (struct iphdr *) (buffer + sizeof (struct ethhdr));
    iphdrlen = iph->ihl * 4;

    udph = (struct udphdr*) (buffer + iphdrlen + sizeof (struct ethhdr));

    cache_ip_header(buffer, msg);

    /* obtem portas da origem e do destino */
    sprintf(msg,
        "%s<srcport>%u</srcport><dstport>%u</dstport><proto>udp</proto>",
        msg,
        ntohs(udph->source),
        ntohs(udph->dest)
    );
}

/*
 * Extrai dados dos pacotes icmp e retorna conteudo em msg
 */
void cache_icmp_packet(const u_char *buffer, char *msg) {
    cache_ip_header(buffer, msg);

    sprintf(msg, "%s<srcport></srcport><dstport></dstport><proto>icmp</proto>", msg);
}

